/**
* @file model_config.cpp
*
* Copyright (c) Huawei Technologies Co., Ltd. 2023. All Rights reserved.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include "model_config.h"
#include "framework/executor_c/ge_executor.h"
#include "log_inner.h"
#include "toolchain/slog.h"
#include "model_desc_internal.h"

typedef aclError (*CheckMdlConfigFunc)(const void *const, const size_t);
typedef aclError (*SetMdlConfigFunc)(aclmdlConfigHandle *const, const void *const);

typedef struct {
  CheckMdlConfigFunc checkFunc;
  SetMdlConfigFunc setFunc;
} SetMdlConfigParamFunc;

typedef struct {
  aclmdlConfigAttr configAttr;
  SetMdlConfigParamFunc configParamFunc;
} SetMdlConfigParamFuncMap;

aclError CheckMdlLoadType(const void *const attrValue, const size_t valueSize) {
  if (valueSize != sizeof(size_t)) {
    ACL_LOG_INNER_ERROR("[Check][ValueSize]valueSize[%zu] is invalid, it should be %zu",
        valueSize, sizeof(size_t));
    return ACL_ERROR_INVALID_PARAM;
  }
  const size_t type = *((size_t *)attrValue);
  if ((type < (size_t)ACL_MDL_LOAD_FROM_FILE) ||
      (type > (size_t)ACL_MDL_LOAD_FROM_MEM_WITH_MEM)) {
    ACL_LOG_INNER_ERROR("[Check][Type]type[%zu] is invalid, it should be in [%d, %d]",
        type, ACL_MDL_LOAD_FROM_FILE, ACL_MDL_LOAD_FROM_MEM_WITH_MEM);
    return ACL_ERROR_INVALID_PARAM;
  }
  ACL_LOG_INFO("successfully execute CheckMdlLoadType to check aclmdlLoadType[%zu]", type);
  return ACL_SUCCESS;
}

aclError SetMdlLoadType(aclmdlConfigHandle *const handle, const void *const attrValue) {
  const size_t type = *((size_t *)attrValue);
  handle->mdlLoadType = type;
  handle->attrState = handle->attrState | ACL_MDL_LOAD_TYPE_SIZET_BIT;
  ACL_LOG_INFO("successfully execute SetMdlLoadType to set aclmdlLoadType[%zu]", type);
  return ACL_SUCCESS;
}

// compared with CheckMdlLoadPtrAttr, the CheckMdlLoadPtrAttrEx requires the values attrValue point to
// can't be nullptr
aclError CheckMdlLoadPtrAttrEx(const void *const attrValue, const size_t valueSize) {
  ACL_LOG_INFO("start to execute CheckMdlLoadPtrAttrEx.");
  if (valueSize != sizeof(void *)) {
    ACL_LOG_INNER_ERROR("[Check][ValueSize]valueSize[%zu] is invalid, it should be %zu",
        valueSize, sizeof(void *));
    return ACL_ERROR_INVALID_PARAM;
  }
  void *val = *((void **)attrValue);
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(val);
  ACL_LOG_INFO("successfully execute CheckMdlLoadPtrAttrEx");
  return ACL_SUCCESS;
}

aclError SetMdlLoadPath(aclmdlConfigHandle *const handle, const void *const attrValue) {
  handle->loadPath = *(char **)attrValue;
  handle->attrState = handle->attrState | ACL_MDL_PATH_PTR_BIT;
  ACL_LOG_INFO("successfully execute SetMdlLoadPath to set loadPath[%s]", handle->loadPath);
  return ACL_SUCCESS;
}

aclError SetMdlLoadMemPtr(aclmdlConfigHandle *const handle, const void *const attrValue) {
  handle->mdlAddr = *(void **)attrValue;
  handle->attrState = handle->attrState | ACL_MDL_MEM_ADDR_PTR_BIT;
  return ACL_SUCCESS;
}

aclError CheckMdlLoadSizeAttr(const void *const attrValue, const size_t valueSize) {
  (void)attrValue;
  if (valueSize != sizeof(size_t)) {
      ACL_LOG_INNER_ERROR("[Check][ValueSize]valueSize[%zu] is invalid, it should be %zu",
          valueSize, sizeof(size_t));
      return ACL_ERROR_INVALID_PARAM;
  }
  return ACL_SUCCESS;
}

aclError SetMdlLoadWeightSize(aclmdlConfigHandle *const handle, const void *const attrValue) {
  const size_t weightSize = *((size_t *)attrValue);
  handle->weightSize = weightSize;
  ACL_LOG_INFO("successfully execute SetMdlLoadWeightSize to set weightSize[%zu]", weightSize);
  return ACL_SUCCESS;
}

aclError SetMdlLoadMemSize(aclmdlConfigHandle *const handle, const void *const attrValue) {
  const size_t memSize = *((size_t *)attrValue);
  handle->mdlSize = memSize;
  handle->attrState = handle->attrState | ACL_MDL_MEM_SIZET_BIT;
  ACL_LOG_INFO("successfully execute SetMdlLoadMemSize to set memSize[%zu]", memSize);
  return ACL_SUCCESS;
}

aclError CheckMdlLoadPtrAttr(const void *const attrValue, const size_t valueSize) {
  (void)attrValue;
  if (valueSize != sizeof(void *)) {
    ACL_LOG_INNER_ERROR("[Check][ValueSize]valueSize[%zu] is invalid, it should be %zu",
        valueSize, sizeof(void *));
    return ACL_ERROR_INVALID_PARAM;
  }
  return ACL_SUCCESS;
}

aclError SetMdlLoadWeightPtr(aclmdlConfigHandle *const handle, const void *const attrValue) {
  handle->weightPtr = *(void **)attrValue;
  return ACL_SUCCESS;
}

static SetMdlConfigParamFuncMap g_setMdlConfigMap[] = {
  {ACL_MDL_PRIORITY_INT32, {NULL, NULL}},
  {ACL_MDL_LOAD_TYPE_SIZET, {CheckMdlLoadType, SetMdlLoadType}},
  {ACL_MDL_PATH_PTR, {CheckMdlLoadPtrAttrEx, SetMdlLoadPath}},
  {ACL_MDL_MEM_ADDR_PTR, {CheckMdlLoadPtrAttrEx, SetMdlLoadMemPtr}},
  {ACL_MDL_MEM_SIZET, {CheckMdlLoadSizeAttr, SetMdlLoadMemSize}},
  {ACL_MDL_WEIGHT_ADDR_PTR, {CheckMdlLoadPtrAttr, SetMdlLoadWeightPtr}},
  {ACL_MDL_WEIGHT_SIZET, {CheckMdlLoadSizeAttr, SetMdlLoadWeightSize}},
  {ACL_MDL_WORKSPACE_ADDR_PTR, {NULL, NULL}},
  {ACL_MDL_WORKSPACE_SIZET, {NULL, NULL}},
  {ACL_MDL_INPUTQ_NUM_SIZET, {NULL, NULL}},
  {ACL_MDL_INPUTQ_ADDR_PTR, {NULL, NULL}},
  {ACL_MDL_OUTPUTQ_NUM_SIZET, {NULL, NULL}},
  {ACL_MDL_OUTPUTQ_ADDR_PTR, {NULL, NULL}}
};

static bool CheckMdlLoadConfigFromFile(const aclmdlConfigHandle *const handle) {
  if ((handle->attrState & ACL_MDL_PATH_PTR_BIT) == 0) {
    ACL_LOG_INNER_ERROR("[Check][Type]model load type[%zu]: model path is not set in aclmdlConfigHandle",
        handle->mdlLoadType);
    return false;
  }
  return true;
}

static bool CheckMdlLoadConfigFromMem(const aclmdlConfigHandle *const handle) {
  if ((handle->attrState & ACL_MDL_MEM_ADDR_PTR_BIT) == 0) {
    ACL_LOG_INNER_ERROR("[Check][Type]model load type[%zu]: model memory ptr is not set in aclmdlConfigHandle",
        handle->mdlLoadType);
    return false;
  }

  if ((handle->attrState & ACL_MDL_MEM_SIZET_BIT) == 0) {
    ACL_LOG_INNER_ERROR("[Check][Type]model load type[%zu]: model memory size is not set in aclmdlConfigHandle",
        handle->mdlLoadType);
    return false;
  }
  return true;
}

bool CheckMdlConfigHandle(const aclmdlConfigHandle *const handle) {
  if ((handle->attrState & ACL_MDL_LOAD_TYPE_SIZET_BIT) == 0) {
    ACL_LOG_INNER_ERROR("[Find][Type]model load type is not set in aclmdlConfigHandle");
    return false;
  }

  if ((handle->mdlLoadType == (size_t)ACL_MDL_LOAD_FROM_FILE) ||
      (handle->mdlLoadType == (size_t)ACL_MDL_LOAD_FROM_FILE_WITH_MEM)) {
    if (!CheckMdlLoadConfigFromFile(handle)) {
      return false;
    }
  }

  if ((handle->mdlLoadType == (size_t)ACL_MDL_LOAD_FROM_MEM) ||
      (handle->mdlLoadType == (size_t)ACL_MDL_LOAD_FROM_MEM_WITH_MEM)) {
    if ((!CheckMdlLoadConfigFromMem(handle))) {
      return false;
    }
  }
  return true;
}

aclError aclmdlSetConfigOpt(aclmdlConfigHandle *handle, aclmdlConfigAttr attr,
                            const void *attrValue, size_t valueSize) {
  ACL_LOG_INFO("start to execute aclmdlSetConfigOpt, attr[%d]", (int32_t)attr);
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(handle);
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(attrValue);
  SetMdlConfigParamFunc paramFunc;
  uint32_t attrCount = sizeof(g_setMdlConfigMap)/ sizeof(SetMdlConfigParamFuncMap);
  if (attr >= attrCount) {
    ACL_LOG_INNER_ERROR("attr set invalid.");
    return ACL_ERROR_INVALID_PARAM;
  }
  if (attr == g_setMdlConfigMap[attr].configAttr) {
    paramFunc = g_setMdlConfigMap[attr].configParamFunc;
  } else {
    ACL_LOG_INNER_ERROR("[Find][Attr]attr[%d] is invalid. it should be [%d, %d]", (int32_t)attr,
        (int32_t)ACL_MDL_PRIORITY_INT32, (int32_t)ACL_MDL_OUTPUTQ_ADDR_PTR);
    return ACL_ERROR_INVALID_PARAM;
  }
  if (paramFunc.checkFunc != NULL) {
    aclError ret = paramFunc.checkFunc(attrValue, valueSize);
    if (ret != ACL_SUCCESS) {
      ACL_LOG_INNER_ERROR("[Check][Params]check params by aclmdlSetConfigOpt error, result[%d], attr[%d], "
          "valueSize[%zu]", ret, (int32_t)attr, valueSize);
      return ret;
    }
  } else {
    ACL_LOG_INNER_ERROR("not support set this attr.");
    return ACL_ERROR_INVALID_PARAM;
  }

  if (paramFunc.setFunc != NULL) {
    aclError ret = paramFunc.setFunc(handle, attrValue);
    if (ret != ACL_SUCCESS) {
      ACL_LOG_INNER_ERROR("[Set][Params]set params by aclmdlSetConfigOpt error, result[%d], attr[%d]", ret,
          (int32_t)attr);
      return ret;
    }
  } else {
    ACL_LOG_INNER_ERROR("not support set this attr.");
    return ACL_ERROR_INVALID_PARAM;
  }

  ACL_LOG_INFO("successfully execute aclmdlSetConfigOpt, attr[%d]", (int32_t)attr);
  return ACL_SUCCESS;
}

aclmdlConfigHandle *aclmdlCreateConfigHandle() {
  aclmdlConfigHandle *configHandle = (aclmdlConfigHandle *)malloc(sizeof(aclmdlConfigHandle));
  if (configHandle == NULL) {
    ACL_LOG_INNER_ERROR("malloc memory failed, create config handle failed.");
    return NULL;
  }
  configHandle->loadPath = NULL;
  configHandle->mdlAddr = NULL;
  configHandle->weightPtr = NULL;
  configHandle->priority = 0;
  configHandle->mdlLoadType = 0UL;
  configHandle->mdlSize = 0UL;
  configHandle->weightSize = 0UL;
  configHandle->attrState = 0UL;
  return configHandle;
}

aclError aclmdlDestroyConfigHandle(aclmdlConfigHandle *handle) {
  ACL_REQUIRES_NOT_NULL_WITH_INPUT_REPORT(handle);
  ACL_DELETE_AND_SET_NULL(handle);
  return ACL_SUCCESS;
}
